import os, os.path, sys, wx
from wx import xrc
import wx.aui

import matplotlib
matplotlib.use('WXAgg')
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import pylab

from Classifier import Classifier
from Runcase import Runcase

class SVMs(Classifier):


    options = {'train_file_textctrl':{ 'name':'trainFile', 'value':None, 'default':''},
            'test_file_textctrl':{ 'name':'testFile','value':None,  'default':''},
            'search_method_combobox':{ 'name':'searchMethod','value':None,  'default':'Grid Search'},
            #'search_method_combobox':{ 'name':'searchMethod','value':None,  'default':'GA'},
            'search_best_feartures_checkbox':{ 'name':'searchBestFeartures','value':None,  'default':False},
            'plot_parameter_contour_checkbox':{ 'name':'plotParameterContour','value':None,  'default':False},
            'plot_ga_curve_checkbox':{ 'name':'plotGACurve','value':None,  'default':False},
            'svm_type_radiobox':{ 'name':'svmType','value':None,  'default':0},
            'epsilon_textctrl':{ 'name':'epsilon','value':None,  'default':'0.001'},
            'nfold_spinctrl':{ 'name':'nfold','value':None,  'default':5},
            'kernel_type_combobox':{ 'name':'kernelType','value':None,  'default':'rbf'},
            'gamma_textctrl':{ 'name':'gamma','value':None,  'default':''},
            'degree_textctrl':{ 'name':'degree','value':None,  'default':'3'},
            'coef0_textctrl':{ 'name':'coef0','value':None,  'default':'0'},
            'lower_limit_spinctrl':{ 'name':'lowerLimit','value':None,  'default':-1},
            'upper_limit_spinctrl':{ 'name':'upperLimit','value':None,  'default':1},
            'cost_textctrl':{ 'name':'cost','value':None,  'default':''},
            'nu_textctrl':{ 'name':'nu','value':None,  'default':'0.5'},
            'log2gamma_begin_textctrl':{ 'name':'log2gamma_begin','value':None,  'default':'3'},
            'log2gamma_end_textctrl':{ 'name':'log2gamma_end','value':None,  'default':'-15'},
            'log2gamma_step_textctrl':{ 'name':'log2gamma_step','value':None,  'default':'-2'},
            'log2cost_begin_textctrl':{ 'name':'log2cost_begin','value':None,  'default':'-5'},
            'log2cost_end_textctrl':{ 'name':'log2cost_end','value':None,  'default':'15'},
            'log2cost_step_textctrl':{ 'name':'log2cost_step','value':None,  'default':'2'},
            'epsilon_SVR_textctrl':{ 'name':'epsilon_SVR', 'value':None, 'default':'0.1'},
            #GA Options
            'init_population_textctrl':{ 'name':'initPopulation', 'value':None, 'default':'10'},
            #'init_population_textctrl':{ 'name':'initPopulation', 'value':None, 'default':'4'},
            'num_new_organisms_textctrl':{ 'name':'numNewOrganisms', 'value':None, 'default':'0'},
            'mut_prob_textctrl':{ 'name':'mutProb', 'value':None, 'default':'0.01'},
            #'child_count_textctrl':{ 'name':'childCount', 'value':None, 'default':'100'},
            'child_count_textctrl':{ 'name':'childCount', 'value':None, 'default':'40'},
            'child_cull_textctrl':{'name':'childCull', 'value':None, 'default':'20'},
            #'child_cull_textctrl':{'name':'childCull', 'value':None, 'default':'4'},
            'mutants_textctrl':{'name':'mutants', 'value':None, 'default':'0.1'},
            'mutate_after_mating_checkbox':{ 'name':'mutateAfterMating', 'value':None, 'default':True},
            'incest_textctrl':{ 'name':'incest', 'value':None, 'default':'10'},
            #'incest_textctrl':{ 'name':'incest', 'value':None, 'default':'2'},
            'desired_rate_textctrl':{ 'name':'desiredRate', 'value':None, 'default':'95'}
            }

    def __init__(self, dataDisplayer):
        #super(SVMs, self).__init__(dataDisplayer)
        Classifier.__init__(self, dataDisplayer)


    def initUI(self):


        """ set the gui control value to their defaults"""
        for controlName in self.options.keys():
            self.setControlValue(controlName, self.getOptionDefault(controlName=controlName))

        """set option value to their defaults"""
        for controlName in self.options.keys():
            self.setOptionValue(controlName=controlName, value=self.getOptionDefault(controlName=controlName))

        self.initKernelFuncImg()

        self.mgr = wx.aui.AuiManager(self.dataDisplayer.getControl('SVMs_panel'))
        #self.mgr.SetManagedWindow(self.dataDisplayer.getControl('SVMs_panel'))
        #self.mgr.SetManagedWindow(self.dataDisplayer.getFrame())
        runPanel = self.dataDisplayer.getControl('run_panel')
        svmsPanel = self.dataDisplayer.getControl('svms_panel')
        gaPanel = self.dataDisplayer.getControl('ga_panel')

        self.mgr.AddPane(runPanel, wx.aui.AuiPaneInfo().Left().MaximizeButton(True).MinimizeButton(True))
        self.mgr.AddPane(svmsPanel, wx.aui.AuiPaneInfo().Center().MaximizeButton(True).MinimizeButton(True))
        self.mgr.AddPane(gaPanel, wx.aui.AuiPaneInfo().Right().MaximizeButton(True).MinimizeButton(True))

        self.mgr.Update()

        self.mgr2 = wx.aui.AuiManager(self.dataDisplayer.getControl('ga_panel'))
        #self.mgr2.SetManagedWindow(self.dataDisplayer.getControl('ga_panel'))
        gaOptionPanel = self.dataDisplayer.getControl('ga_option_panel')
        graphNotebook = self.dataDisplayer.getControl('graph_notebook')
        self.mgr2.AddPane(gaOptionPanel, wx.aui.AuiPaneInfo().Center().MaximizeButton(True).MinimizeButton(True))
        self.mgr2.AddPane(graphNotebook, wx.aui.AuiPaneInfo().Center().MaximizeButton(True).MinimizeButton(True))
        self.mgr2.Update()



    def bindEvents(self):
        for controlName in self.options.keys():
            control = self.dataDisplayer.getControl(controlName)
            self.dataDisplayer.getFrame().Bind(self.getEventType(controlName=controlName), self.updateOptions, self.dataDisplayer.getControl(controlName))

        self.dataDisplayer.getControl('svm_type_radiobox').Bind(wx.EVT_RADIOBOX, self.svmTypeChanged)
        self.dataDisplayer.getControl('run_test_checkbox').Bind(wx.EVT_CHECKBOX, self.checkToToogle)
        self.dataDisplayer.getControl('cost_checkbox').Bind(wx.EVT_CHECKBOX, self.checkToToogle)
        self.dataDisplayer.getControl('gamma_checkbox').Bind(wx.EVT_CHECKBOX, self.checkToToogle)
        self.dataDisplayer.getControl('mutate_after_mating_checkbox').Bind(wx.EVT_CHECKBOX, self.checkToToogle)
        self.dataDisplayer.getControl('train_file_browse_button').Bind(wx.EVT_BUTTON, self.openFile)
        self.dataDisplayer.getControl('test_file_browse_button').Bind(wx.EVT_BUTTON, self.openFile)
        self.dataDisplayer.getControl('run_button').Bind(wx.EVT_BUTTON, self.run)
        self.dataDisplayer.getControl('search_method_combobox').Bind(wx.EVT_COMBOBOX, self.searchMethodChanged)
        self.dataDisplayer.getControl('plot_parameter_contour_checkbox').Bind(wx.EVT_CHECKBOX, self.plotStateChanged)
        self.dataDisplayer.getControl('plot_ga_curve_checkbox').Bind(wx.EVT_CHECKBOX, self.plotStateChanged)


    def plotStateChanged(self, event):
        name = event.GetEventObject().GetName()
        if name == 'plot_parameter_contour_checkbox':
            if self.getControlValue(name) == True:
                self.dataDisplayer.initContour()
            else:
                self.dataDisplayer.deleteContour()
        elif name == 'plot_ga_curve_checkbox':
            if self.getControlValue(name) == True:
                self.dataDisplayer.initGACurve()
            else:
                self.dataDisplayer.deleteGaCurve()
        else:
            pass

        event.Skip()

    def searchMethodChanged(self, event):
        """docstring for searchMethodChanged"""
        searchMethod = self.getControlValue(event.GetEventObject().GetName())
        if searchMethod == 'Grid Search':
            self.dataDisplayer.getControl('plot_parameter_contour_checkbox').Enable(True)
            self.dataDisplayer.getControl('plot_ga_curve_checkbox').Enable(False)
            self.dataDisplayer.getControl('graph_notebook').SetSelection(0)
        else:
            self.dataDisplayer.getControl('plot_parameter_contour_checkbox').Enable(False)
            self.dataDisplayer.getControl('plot_ga_curve_checkbox').Enable(True)
            self.dataDisplayer.getControl('graph_notebook').SetSelection(1)
        event.Skip()


    def svmTypeChanged(self, event):
        """change cost_textctrl, cost_checkbox, nu_textctrl state"""
        choice = event.GetSelection()
        self.actions = [{'nu_textctrl':False, 'nu_statictext':False, 'cost_checkbox':True, 'cost_textctrl':False, 'epsilon_SVR_textctrl':False, 'epsilon_SVR_statictext':False},
                {'nu_textctrl':True, 'nu_statictext':True, 'cost_checkbox':False, 'cost_textctrl':False, 'epsilon_SVR_textctrl':False, 'epsilon_SVR_statictext':False},
                {'nu_textctrl':True, 'nu_statictext':True, 'cost_checkbox':False, 'cost_textctrl':False, 'epsilon_SVR_textctrl':False, 'epsilon_SVR_statictext':False},
                {'nu_textctrl':False, 'nu_statictext':False, 'cost_checkbox':True, 'cost_textctrl':False, 'epsilon_SVR_textctrl':True, 'epsilon_SVR_statictext':True},
                {'nu_textctrl':True, 'nu_statictext':True, 'cost_checkbox':True, 'cost_textctrl':False, 'epsilon_SVR_textctrl':False, 'epsilon_SVR_statictext':False}]

        for controlName, value in self.actions[choice].iteritems():
            control = self.dataDisplayer.getControl(controlName)
            if isinstance(control, wx.TextCtrl):
                if value == False :
                    control.Clear()
                    control.Enable(False)
                else:
                    control.Enable(True)
                    self.setControlValue(controlName, self.getOptionDefault(controlName))
            else:
                control.Enable(value)

        event.Skip()


    def checkToToogle(self, event):
        """when the event source object(a checkbox) is checked, enable or disable corresponding controls"""
        checkbox = event.GetEventObject()
        checkboxName = checkbox.GetName()

        checkToEnable = {'run_test_checkbox':['test_file_textctrl' ,'test_file_browse_button'] ,'cost_checkbox':['cost_textctrl'], 'gamma_checkbox':['gamma_textctrl'],
                'mutate_after_mating_checkbox':['mutants_textctrl'] }
        checkToDisable = {'gamma_checkbox':['log2gamma_begin_statictext', 'log2gamma_end_statictext', 'log2gamma_step_statictext','log2gamma_begin_textctrl', 'log2gamma_end_textctrl', 'log2gamma_step_textctrl'],
                        'cost_checkbox':['log2cost_begin_statictext', 'log2cost_end_statictext', 'log2cost_step_statictext','log2cost_begin_textctrl', 'log2cost_end_textctrl', 'log2cost_step_textctrl'],
                        'mutate_after_mating_checkbox':['mutants_textctrl', 'mutants_statictext'] }

        if checkbox.IsChecked():
            for controlName in checkToEnable[checkboxName]:
                self.dataDisplayer.getControl(controlName).Enable(True)
            if checkboxName in checkToDisable.keys():
                for controlName in checkToDisable[checkboxName]:
                    control = self.dataDisplayer.getControl(controlName)
                    if isinstance(control, wx.TextCtrl):
                        control.Clear()
                    control.Enable(False)
        else:
            for controlName in checkToEnable[checkboxName]:
                control = self.dataDisplayer.getControl(controlName)
                control.Enable(False)
                if isinstance(control, wx.TextCtrl):
                    control.Clear()
            if checkboxName in checkToDisable.keys():
                for controlName in checkToDisable[checkboxName]:
                    control = self.dataDisplayer.getControl(controlName)
                    control.Enable(True)
                    if isinstance(control, wx.TextCtrl):
                        self.setControlValue(controlName, self.getOptionDefault(controlName))


        event.Skip()
    def initKernelFuncImg(self):
        self.kernelFuncStr = {'linear':r'$K(x_i,x_j) = x_i^T x_j$', 'polynomial':r'$K(x_i,x_j) = (\gamma x_i^T x_j + r)^d , \gamma > 0$', 'rbf':r'$K(x_i,x_j) = exp(-\gamma \parallel x_i - x_j \parallel^2), \gamma > 0$', 'sigmoid':r'$K(x_i,x_j) = \tanh(\gamma x_i^T x_j + r)$'}
        kernel_function_statictext = self.dataDisplayer.getControl('kernel_function_statictext')

        #color = kernel_function_statictext.GetParent().GetBackgroundColour()
        #rgb = color.Get()
        #self.color = '#'+hex(rgb[0])[2:]+hex(rgb[1])[2:]+hex(rgb[2])[2:]
        self.color = '#F5F3EF'

        self.kernelFuncImg = Figure()
        self.dpi = self.kernelFuncImg.get_dpi()
        self.kernelFuncImg.set_figheight(30.0/self.dpi + 0.1)
        self.kernelFuncImg.set_figwidth(500/self.dpi)

        self.kernelFuncImg.set_facecolor(self.color)
        self.kernelFuncImg.set_edgecolor(self.color)

        kernelType = self.getOptionDefault(controlName='kernel_type_combobox')

        self.canvas = FigureCanvas(kernel_function_statictext.GetParent(), -1, self.kernelFuncImg)
        self.canvas.SetBackgroundColour(wx.NamedColor("WHITE"))
        kernel_function_statictext.GetContainingSizer().Add(self.canvas, 1, wx.ALL | wx.EXPAND )
        self.drawKernelFunImg(self.kernelFuncStr[kernelType])


    def drawKernelFunImg(self, kernelFuncStr):
        """docstring for drawKernelFunImg"""
        self.kernelFuncImg.clear()

        ax = self.kernelFuncImg.add_subplot(111)
        ax.set_axis_bgcolor(self.color)
        for loc, spine in ax.spines.iteritems():
            spine.set_color('none')
        ax.xaxis.set_ticks([])
        ax.yaxis.set_ticks([])

        ax.text(0.05, 0.2, kernelFuncStr, {'color' : 'black', 'fontsize' : 16})
        self.canvas.draw()


    def updateOptions(self, event):
        source = event.GetEventObject()
        controlName = source.GetName()
        self.setOptionValue(controlName=controlName, value=self.getControlValue(controlName=controlName))
        self.setControlValue(controlName='console_textctrl', value = '%s:%s' % (self.options[controlName]['name'], self.getOptionValue(controlName)))

        self.updateKernelParameters(source)
        self.updateKernelFuncImg(source)
        event.Skip()

    def updateKernelFuncImg(self, source):
        if source.GetName() not in ['kernel_type_combobox', 'gamma_textctrl', 'degree_textctrl', 'coef0_textctrl']:
            return

        kernelFuncStr = self.kernelFuncStr[self.getControlValue('kernel_type_combobox')]
        if self.dataDisplayer.getControl('gamma_textctrl').IsEnabled():
            gamma = self.dataDisplayer.getControl('gamma_textctrl').GetValue()
            if gamma != '' and float(gamma) > 0:
                kernelFuncStr = kernelFuncStr.replace(', \gamma > 0','')
                kernelFuncStr = kernelFuncStr.replace('\gamma', gamma)

        if self.dataDisplayer.getControl('degree_textctrl').IsEnabled():
            degree = self.dataDisplayer.getControl('degree_textctrl').GetValue()
            if degree != '' and int(degree) <= 9:
                kernelFuncStr = kernelFuncStr.replace('d', degree)

        if self.dataDisplayer.getControl('coef0_textctrl').IsEnabled():
            coef0 = self.dataDisplayer.getControl('coef0_textctrl').GetValue()
            if coef0 != '':
                if coef0 == '0' or coef0 == '-0' or coef0 == '+0':
                    kernelFuncStr = kernelFuncStr.replace('+ r','')
                elif '-' in coef0:
                    kernelFuncStr = kernelFuncStr.replace('+ r', coef0)
                else:
                    kernelFuncStr = kernelFuncStr.replace('r', coef0)

        #ax.text(0.05, 0.2, kernelFuncStr, {'color' : 'black', 'fontsize' : 16})
        #self.canvas.draw()
        self.drawKernelFunImg(kernelFuncStr)


    def updateKernelParameters(self, source):
        if not source is self.dataDisplayer.getControl('kernel_type_combobox'):
            return

        self.checkParameters = {
                'linear':{'gamma_checkbox':False, 'gamma_textctrl':False, 'degree_statictext':False, 'degree_textctrl':False, 'coef0_statictext':False, 'coef0_textctrl':False, 'log2gamma_begin_statictext':False, 'log2gamma_end_statictext':False, 'log2gamma_step_statictext':False,'log2gamma_begin_textctrl':False, 'log2gamma_end_textctrl':False, 'log2gamma_step_textctrl':False},
                'polynomial':{'gamma_checkbox':True, 'gamma_textctrl':False, 'degree_statictext':True, 'degree_textctrl':True, 'coef0_statictext':True, 'coef0_textctrl':True, 'log2gamma_begin_statictext':True, 'log2gamma_end_statictext':True, 'log2gamma_step_statictext':True,'log2gamma_begin_textctrl':True, 'log2gamma_end_textctrl':True, 'log2gamma_step_textctrl':True},
                'rbf':{'gamma_checkbox':True, 'gamma_textctrl':False, 'degree_statictext':False, 'degree_textctrl':False, 'coef0_statictext':False, 'coef0_textctrl':False, 'log2gamma_begin_statictext':True, 'log2gamma_end_statictext':True, 'log2gamma_step_statictext':True,'log2gamma_begin_textctrl':True, 'log2gamma_end_textctrl':True, 'log2gamma_step_textctrl':True},
                'sigmoid':{'gamma_checkbox':True, 'gamma_textctrl':False, 'degree_statictext':False, 'degree_textctrl':False, 'coef0_statictext':True, 'coef0_textctrl':True,'log2gamma_begin_statictext':True, 'log2gamma_end_statictext':True, 'log2gamma_step_statictext':True,'log2gamma_begin_textctrl':True, 'log2gamma_end_textctrl':True, 'log2gamma_step_textctrl':True}
                }
        for controlName, value in self.checkParameters[self.getControlValue(controlName='kernel_type_combobox')].iteritems():
            self.dataDisplayer.getControl(controlName).Enable(value)

            if self.dataDisplayer.getControl(controlName).IsEnabled():
                control = self.dataDisplayer.getControl(controlName)
                if isinstance(control, wx.TextCtrl):
                    self.setControlValue(controlName=controlName, value=self.getOptionDefault(controlName=controlName))
            else:
                control = self.dataDisplayer.getControl(controlName)
                if isinstance(control, wx.TextCtrl):
                    self.dataDisplayer.getControl(controlName).Clear()

        self.setControlValue(controlName='gamma_checkbox', value=False)


    def setControlValue(self, controlName, value):
        """docstring for setControlValue"""
        control = self.dataDisplayer.getControl(controlName)
        if control.IsEnabled():
            if isinstance(control, wx.RadioBox):
                control.SetSelection(value)
            else:
                control.SetValue(value)


    def getControlValue(self, controlName):
        control = self.dataDisplayer.getControl(controlName)
        if  isinstance(control, wx.RadioBox):
            #return control.GetPageText(control.GetSelection())
            return control.GetSelection()
        if isinstance(control, wx.Button) or isinstance(control, wx.TextCtrl) or isinstance(control, wx.SpinCtrl) or isinstance(control, wx.ComboBox) or isinstance(control, wx.CheckBox):
            return control.GetValue()
        else:
            pass


    def getOptionDefault(self, controlName):
        return self.options[controlName]['default']


    def getOptionName(self, controlName):
        """ get the option name of corresponding controlName"""
        return self.options[controlName]['name']


    def setOptionValue(self, controlName, value):
        """set the option value of corresponding controlName"""
        self.options[controlName]['value'] = value


    def getOptionValue(self, controlName):
        return self.options[controlName]['value']


    def openFile(self, event):
        homeDir = os.getcwd()
        wildcard = "text file (*.txt)|*.py|"     \
                   "All files (*.*)|*.*"
        dlg = wx.FileDialog(
                    self.dataDisplayer.getFrame(), message="Choose a file",
                    defaultDir=os.getcwd(),
                    defaultFile="",
                    wildcard=wildcard,
                    style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR
                    )
        if dlg.ShowModal() == wx.ID_OK:
                path = dlg.GetPath()
                #fileName = os.path.split(path)[1]

                if event.GetEventObject().GetName() == 'train_file_browse_button':
                    self.dataDisplayer.getControl('train_file_textctrl').SetValue(path)
                else:
                    if self.dataDisplayer.getControl('test_file_textctrl').IsEnabled():
                        self.dataDisplayer.getControl('test_file_textctrl').SetValue(path)

        dlg.Destroy()
        os.chdir(homeDir)
        event.Skip()


    def run(self, event):
        optionValues = {}
        for controlName, item in self.options.iteritems():
            optionValues[item['name']] = item['value']

        Runcase(dataDisplayer=self.dataDisplayer, options=optionValues).run()
        event.Skip()
